Listing 1
Giving attributes properties.
Since attributes are actually defined by classes themselves, they can have properties. This attribute's class definition includes a constructor and a property. When you apply this attribute to an element, you must invoke its constructor. Attributes can have several overloaded constructors (with different sets of arguments).

<Serializable(), _
AttributeUsage(AttributeTargets.All
, Inherited:=False, AllowMultiple:=False)> 
Public NotInheritable Class CLSCompliantAttribute
	Inherits Attribute
	Private m_compliant As Boolean
	Public Sub New(ByVal isCompliant As Boolean)
		m_compliant = isCompliant
	End Sub
	Public ReadOnly Property IsCompliant() As Boolean
		Get
			Return m_compliant
		End Get
	End Property
End Class



Listing 2
Giving input forms caption text.
This attribute specifies which property will be used as the caption text for the input form.

<AttributeUsage(AttributeTargets.Class, 
AllowMultiple:=False, Inherited:=True)> _
Public NotInheritable Class DisplayFormCaptionAttribute
	Inherits System.Attribute
	Private _propertyName As String
	Sub New(ByVal PropertyName As String)
		_propertyName = PropertyName
	End Sub
	Public ReadOnly Property PropertyName() As String
		Get
			Return _propertyName
		End Get
    End Property
End Class



Listing 3
You can look but you better not touch.
You can use a ReadOnly property for values you want users to see but not be able to alter on the data entry form. Here's what you'd add to DisplayAttr.vb to do this. Note that you must enclose the ReadOnly property in brackets because ReadOnly is a reserved word in VB.
<AttributeUsage( _AttributeTargets.Property)> Public NotInheritable Class _
DisplayFieldAttribute
	Inherits System.Attribute
	Private _length As Integer
	Private _readOnly As Boolean
	Public Sub New(ByVal Length As Integer)
		_length = Length
	End Sub
	Public Sub New(ByVal Length As Integer, ByVal [ReadOnly] As Boolean)
		_length = Length
		_readOnly = [ReadOnly]
	End Sub
	Public ReadOnly Property Length() As Integer
		Get
			Return _length
		End Get
	End Property
	Public Property [ReadOnly]() As Boolean
		Get
			Return _readOnly
		End Get
		Set(ByVal Value As Boolean)
			_readOnly = Value
		End Set
	End Property
End Class




Listing 4.
Build a Customer object
Here's a sample of a Customer object with a number of properties: an Identifier, a FirstName, a LastName property plus a read-only property, FullName, that retrieves the complete name by concatenating the last and first names. 

<DisplayInForm(), DisplayFormCaption("FullName")> Public Class Customer
	Private _identifier As String
	Private _firstName As String
	Private _lastName As String
	<DisplayField(0, True)> Public Property Identifier() As String
		Get
			Return _identifier
		End Get
		Set(ByVal Value As String)
			_identifier = Value
		End Set
	End Property
	<DisplayField(30)> Public Property FirstName() As String
		Get
			Return _firstName
		End Get
		Set(ByVal Value As String)
			_firstName = Value
		End Set
	End Property
	<DisplayField(30, [ReadOnly]:=False)> Public Property LastName() As String
		Get
			Return _lastName
		End Get
		Set(ByVal Value As String)
			_lastName = Value
		End Set
	End Property
	Public ReadOnly Property FullName() As String
		Get
			Return _lastName & ", " & _lastName
		End Get
	End Property
End Class



Listing 5
Add labels and textboxes
The InitFields function calls AddControl to dynamically add a new label and textbox to the panel for each property, taking into account information fetched from the attribute values.

Imports System.Reflection    

Public Sub InitFields(ByVal Obj As Object)
	Dim DispInForm As DisplayInFormAttribute
	Dim DFCaption As DisplayFormCaptionAttribute
	Dim DispField As DisplayFieldAttribute
	Dim MI As MemberInfo
	Dim Y As Integer
	Dim FieldText As String

	DispInForm = 	Attribute.GetCustomAttribute _
	(Obj.GetType, GetType(DisplayInFormAttribute))
	If Not (DispInForm Is Nothing) Then
		DFCaption = Attribute.GetCustomAttribute( _
		Obj.GetType, GetType (DisplayFormCaptionAttribute))
		If Not (DFCaption Is Nothing) Then
			Me.Text = Obj.GetType.Name & " : " & Obj.GetType.InvokeMember _
			(DFCaption.PropertyName, BindingFlags.GetProperty, _
			Nothing, Obj, Nothing).ToString
		End If
		For Each MI In Obj.GetType.GetProperties
			DispField = _
			Attribute.GetCustomAttribute (MI, GetType(DisplayFieldAttribute))
			If Not (DispField Is Nothing) Then
				FieldText = Obj.GetType. InvokeMember(MI.Name, _
				BindingFlags.GetProperty, Nothing, Obj, Nothing)
				AddControl(MI.Name, FieldText, Y, DispField.ReadOnly, _
				DispField.Length)
				Y += 1
			End If
		Next
	End If
End Sub

Private Sub AddControl(ByVal Caption As String, ByVal Value As String, ByVal _
VPos As Integer, ByVal Locked As Boolean, ByVal Max As Integer)
	Dim l As Label = New Label()
	Dim t As TextBox = New TextBox()
	Me.Panel1.Controls.Add(l)
	Me.Panel1.Controls.Add(t)
	l.Text = Caption
	t.Text = Value
	l.AutoSize = True
	If Max > 0 Then
		t.MaxLength = Max
	Else
		Max = _defaultLength
	End If
	t.Width = Me.CreateGraphics.MeasureString _
	(New String(Chr(64), Max), Me.Font).Width * 0.65
	t.SelectionStart = 0
	If Locked Then t.ReadOnly = True
	l.Location = New Point(10, VPos * (l.Height + t.Height + _VPadding))
	t.Location = New Point(10, l.Location.Y + l.Height)
End Sub




Listing 6
Display your data entry form.
This code creates a Customer object and displays the data entry form when you click the button on the form.
Private MyCustomer As Customer = New Customer()
Private MyDisplayForm As DataForm
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles Button1.Click
	With MyCustomer
		.Identifier = "TD053294"
		.FirstName = "John"
		.LastName = "Doe"
	End With
	MyDisplayForm = New DataForm()
	With MyDisplayForm
		.InitFields(MyCustomer)
		.ShowDialog()
	End With
End Sub



